superstrict

include "../common/tvector.bmx"
include "../find_img_edges_normals/tpixmapnormals.bmx"

global xres#=800
global yres#=600
graphics xres,yres,0,0,2
global pixmap:TPixmap = loadpixmap("bitmap.bmp")
assert pixmap<>null





global imgNorm:TimageNormals= new TimageNormals
imgNorm.findEdges(pixmap)
global screen_width:int = pixmap.width
global screen_height:int = pixmap.height
global edges:int[] = imgNorm.edgemap
global normals:vec2[] = imgNorm.smoothedNormals

local player:TPlayer = new TPlayer
player.pos._x = 10
player.pos._y = 10


global scale# = 4;

global dt#

local tim:int, oldtim:int, dtmillis:int

while not keyhit(KEY_ESCAPE)
	tim = millisecs()
	dtMillis = tim-oldtim
	dt = dtMillis/1000.0
	oldtim = tim
	cls
	
	drawrectopen(0,0,screen_width*scale,screen_height*scale)
	
'	setscale scale,scale
	'drawpixmap(pixmap,0,0)
'	drawimage(img,0,0)
'	setscale 1,1

	'Draw edges and normals
	local i:int
	local j:int
	
	for i=1 until pixmap.width
	for j=1 until pixmap.height
		
		'draw edge pixels. warning: normals are null at non-edge pixels. So, check if pixel
		'exists in edgemap before reading smoothedNormals[]
		local isEdge:int = imgNorm.edgemap[i*pixmap.height+j]
		local isPixmap:int = readpixel(pixmap,i,j) < $ffffffff
		
		if isEdge' or isPixmap
			local scx# = i*scale
			local scy# = j*scale
			setcolor 255,255,255
			drawrect scx,scy,scale,scale
			
			if isEdge
				local vx# = imgNorm.smoothedNormals[i*pixmap.height+j].x*(scale*2)
				local vy# = imgNorm.smoothedNormals[i*pixmap.height+j].y*(scale*2)
	
				local pixcenterx# = scx+scale*.5
				local pixcentery# = scy+scale*.5
				setcolor 0,255,0
				drawline pixcenterx,pixcentery, pixcenterx-vx,pixcentery-vy
			endif
		else if(isPixmap)
			'local scx# = i*scale
			'local scy# = j*scale
			'local c1:int = (i+1)*(j+1);
			'setcolor (128+(c1)*255/15) mod 255,(128+c1*255/15) mod 255,0
			'drawrect scx,scy,scale,scale			
			
		endif
	next
	next
	

	player.draw()
	player.controls()
	player.update()
	
	setcolor 255,255,255
	drawtext ""+dtMillis,0,15
	
	flip
	'delay 16
wend

type TPlayer
	field pos:TVector = new TVector
	field vel:TVector = new TVector
	field acc:TVector = new TVector
	field radi# = 3
	field closestX#, closestY#
	field colliding:int = false
	
	field aimAng# = 0;
	
	field aimDir:TVector = new TVector
	
	method draw()
		if colliding
			setcolor 255,0,0
		else
			setcolor 0,255,0
		endif
		
		drawoval (pos._x-radi)*scale, (pos._y-radi)*scale, 2*radi*scale, 2*radi*scale
		
		local lineLen# = 2*scale
		drawline pos._x*scale, pos._y*scale, (pos._x+aimDir._x*lineLen)*scale, (pos._y+aimDir._y*lineLen)*scale
	end method
	
	method controls()
		'if keydown(KEY_A) vel._x :- 1
		'if keydown(KEY_D) vel._x :+ 1
		'if keydown(KEY_W) vel._y :- 1
		'if keydown(KEY_S) vel._y :+ 1
		'vel.mult(100*dt)
		

		' Mouse look
		'local mx# = mousex()/scale
		'local my# = mousey()/scale
		'drawrect mx*scale-2,my*scale-2,4,4
		'aimDir.set(mx - pos._x, my - pos._y)
		'aimDir.normalize()
		
		'control scheme 2
		local spd# = 0
		if keydown(KEY_W) spd = 1
		if keydown(KEY_S) spd = -1
		if keydown(KEY_A) aimAng:- 180*dt
		if keydown(KEY_D) aimAng:+ 180*dt
		if keydown(KEY_LSHIFT) spd :* 3
		vel.set( cos(aimAng)*spd, sin(aimAng)*spd )
		vel.mult(10*dt)	
		

		aimDir.set( cos(aimAng)*1, sin(aimAng)*1 )
		aimDir.normalize()	
		
		

		
		local brushSize# = 5*scale
		
		local shotPos:TVector = VectorClone(aimDir)
		
		local doPaint:int = false
		local erase:int = false
		local paintColor:int = 0
		if mousedown(1)
			paintColor = 0
			doPaint = true
			shotPos.mult(radi*.7*scale) 'TODO. why do i have to mult by scale here?
		else if mousedown(2)
			paintColor = $00ffffff
			doPaint = true
			erase = true
			shotPos.mult(radi*.5*scale) 'TODO. why do i have to mult by scale here? also. when erasing, fill closer to player.
		endif
		
		
		
		local shot:TVector = VectorAdd( shotPos, pos )
		
		if(doPaint)
			local outOfBounds:int =  shot._x < 1 or shot._x >= screen_width-1 or shot._y < 1 or shot._y >= screen_height-1
			if not outOfBounds

			
				imgNorm.findEdges(pixmap)
				edges = imgNorm.edgemap
				normals = imgNorm.smoothedNormals
				
				'drawrectopen( (shot._x-1)*scale,(shot._y-1)*scale,(shot._x+1)*scale,(shot._y+1)*scale);
				if(erase) 'bit larger brush when erasing
					brushSize :* 1.2
					drawCircle(shot._x*scale,shot._y*scale,brushSize)
					paintbrush(shot._x, shot._y, 1.5*radi, paintColor, pixmap)
				else
					drawCircle(shot._x*scale,shot._y*scale,brushSize)
					paintbrush(shot._x, shot._y, 1.2*radi, paintColor, pixmap)
				endif
				
			endif
		endif
		
	end method
	
	method update()
		colliding = false
		

		
		local steps:int
		local numsteps:int = 1
		
		local velPartial:TVector = new TVector
		velPartial.copy(vel)
		'velPartial.mult(dt)
		velPartial.mult(1.0/numsteps)
		
		for steps=0 until numsteps
			pos.add(velPartial)
			'vel._x :+ acc._x * dt
			'vel._y :+ acc._y * dt

			'pos._x :+ vel._x * dt
			'pos._y :+ vel._y * dt
			
			acc.set(0,0)
			
			local outOfScreen:int = pos._x < radi or pos._x >= screen_width-radi or..
							 pos._y < radi or pos._y >= screen_height-radi
			if outOfScreen
				pos.sub(velPartial)
			endif
			
			'Find colliding pixel
			closestx = -1; closesty = -1;
			local minx:int = int(pos._x - radi*2)
			local miny:int = int(pos._y - radi*2)
			local maxx:int = int(pos._x + radi*2)
			local maxy:int = int(pos._y + radi*2)
			minx = max(0, min(screen_width-1,minx))
			miny = max(0, min(screen_height-1,miny))
			maxx = max(0, min(screen_width-1,maxx))
			maxy = max(0, min(screen_height-1,maxy))
			
			drawrectopen(minx*scale,miny*scale,maxx*scale,maxy*scale);
			
			
			local mind# = 1e32
			local i#
			local j#
			for i=minx until maxx
			for j=miny until maxy
				if edges[i * screen_height + j]
					local d# = (pos._x - i)^2 + (pos._y - j)^2
					
					if d < mind
						mind = d
						closestx = i
						closesty = j
					endif
				endif
			next
			next
			
			local closestDistValid:int = closestx <> -1 and closesty <> -1
			if closestDistValid
			
				drawline(pos._x*scale, pos._y*scale, closestx*scale,closesty*scale);
				
				if(mind < (radi*radi) )
					local dist# = sqr(mind)
					
					'pos.sub(velPartial)
					
					if( 1 )
						local norm:vec2 = normals[closestx*screen_height+closesty]
						if norm.x = 0 and norm.y = 0
						else
						
						colliding = true
						drawline(closestx*scale,closesty*scale,..
						         closestx*scale+norm.x*5,closesty*scale+norm.y*5)
						
						local bentNormal:TVector = VectorCreate(norm.x,norm.y)
						
						pos.sub(bentNormal)
						outOfScreen= pos._x < radi or pos._x >= screen_width-radi or..
							 pos._y < radi or pos._y >= screen_height-radi
						if outOfScreen
							pos.add(bentNormal)
						endif		
				
						endif
					endif
				endif
				
			endif
			
			'vel.mult(.5)
			
		next
		vel.set(0,0)
	
	end method
end type

Function drawrectopen(x0#,y0#,x1#,y1#)
	DrawLine(x0,y0,x1,y0)
	DrawLine(x1,y0,x1,y1)
	DrawLine(x1,y1,x0,y1)
	DrawLine(x0,y1,x0,y0)
End Function

'taken from Sol's graphics tutorial at sol.gfxile.net
function paintbrush(x#,y#,r:int,c:int,pixmap:TPixmap)
   local i#, j:int
   for i=0 until 2*r step 1
      local length:int = int( sqr( cos(0.5*180*(i-r)/r)) * r * 2 );
      for j=0 until length
         local xp:int = floor(x+j-length/2.0 + 0.5);
         local yp:int = floor(y-r+i + 0.5);
			local width:int = pixmap.width
			local height:int = pixmap.height
         if(xp > 0 and  xp < (width-1) and yp > 0 and  yp < (height-1))
            writepixel(pixmap,xp,yp,c)
         endif
      next
   next
end function


'circle outline func by ImaginaryHuman
'http://www.blitzbasic.com/codearcs/codearcs.php?code=1476
function drawCircle(xCenter:Int=320, yCenter:Int=240, radius:Int)
	Local p:int,x:int,y:Int
	x=0
	y=radius
	Plot xCenter+x,yCenter+y
	Plot xCenter-x,yCenter+y
	Plot xCenter+x,yCenter-y
	Plot xCenter-x,yCenter-y
	Plot xCenter+y,yCenter+x
	Plot xCenter-y,yCenter+x
	Plot xCenter+y,yCenter-x
	Plot xCenter-y,yCenter-x
	p=1-radius
	While x<y
		If p<0
			x:+1
		Else
			x:+1
			y:-1
		EndIf
		If p<0
			p=p+(x Shl 1)+1
		Else
			p=p+((x-y) Shl 1)+1
		EndIf
		Plot xCenter+x,yCenter+y
		Plot xCenter-x,yCenter+y
		Plot xCenter+x,yCenter-y
		Plot xCenter-x,yCenter-y
		Plot xCenter+y,yCenter+x
		Plot xCenter-y,yCenter+x
		Plot xCenter+y,yCenter-x
		Plot xCenter-y,yCenter-x
	wend
End function

